package com.ludomancy.hugealien;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;

import com.ludomancy.gamelib.Accumulator;
import com.ludomancy.gamelib.Sound;

public class Soul
{
	// moods
	public static final int MOOD_OK = 0;
	public static final int MOOD_HAPPY = 1;
	public static final int MOOD_HEARTBROKEN = 2;
	public static final int MOOD_SHOCKED = 3;
	
	// relationships
	public HashSet relationships = new HashSet();
	
	// events
	public static final int EVENT_NONE = 0;
	public static final int EVENT_LOST_RELATIONSHIP = 1;
	public static final int EVENT_DEATH = 2;
	public static final int EVENT_SUICIDE = 3;
	public static final int EVENT_NEW_LOVE = 4;
	private Accumulator shockTimer = null;
	public int shockType;
	
	// impulses to carry out
	private boolean suicide = false;
	
	// memory
	public HashSet oldRelationships = new HashSet();
	
	// helpers for queries
	public HashSet newRelationships = new HashSet();
	
	private int lastSplitTimestamp;

	public Soul()
	{
	}
	
	public void startRelationship(Soul other, boolean markAsNew)
	{
		relationships.add(other);
		if(markAsNew)
		{
			newRelationships.add(other);
		}
	}
	
	public void update()
	{
		if(shockType != EVENT_NONE)
		{
			shockTimer.update();
			if(shockTimer.isDone())
			{
				if(shockType == EVENT_SUICIDE)
				{
					suicide = true;
				}
				else
				if(shockType == EVENT_NEW_LOVE)
				{
					shockType = EVENT_NONE;
				}
				else
				{
					shockType = EVENT_NONE;
				}
//				newRelationships.clear();
				return;
			}
		}
		
		// if alone, seek a new relationship
		if(relationships.size() == 0 && !Game.game.isOver)
		{
			if(Game.game.soulWorld.timeCounter - lastSplitTimestamp > 3)
			{
				// it's decided: kill self
				shock(EVENT_SUICIDE);
			}
			else
			if(!Game.game.soulWorld.isThereAShockedSoul())
			{
				Vector others = Game.game.soulWorld.getCloseSouls(this);
				for (int i = 0; i < others.size(); i++)
				{
					Soul other = (Soul) others.elementAt(i);
					if(	this != other && 
						!SoulWorld.areTooClose(this, other) && other.getMood() != MOOD_SHOCKED)
					{
						// no coming backs
						if(other.canStartRelationship(this) && this.canStartRelationship(other))
						{
							this.startRelationship(other, true);
							other.startRelationship(this, true);
							Game.game.city.addStar(new Star(this, other, Star.TYPE_GOLD));
							// happy happy joy joy!
							shock(EVENT_NEW_LOVE);
							other.shock(EVENT_NEW_LOVE);
							Sound.play("heartbeat.wav");
							// only one, please
							break;
						}
					}
				}
			}
		}
	}
	
	public boolean isSuicidal()
	{
		return suicide;
	}
	
	public int getMood()
	{
		if(shockType != EVENT_NONE)
		{
			return MOOD_SHOCKED;
		}
		else
		if(relationships.size() == 0)
		{
			return MOOD_HEARTBROKEN;
		}
		return MOOD_OK;
	}
	
	public boolean hasInitialRelationships()
	{
		for (Iterator iterator = relationships.iterator(); iterator.hasNext();)
		{
			Soul s = (Soul) iterator.next();
			if(!newRelationships.contains(s) && !relationships.isEmpty())
			{
				return true;
			}
		}
		return false;
	}
	
	public boolean hasRelationship(Soul other)
	{
		return relationships.contains(other);
	}

	public void onRelationshipLost(Soul other)
	{
		// we broke up...
		shock(EVENT_LOST_RELATIONSHIP);
		// I'll remember you...
		oldRelationships.add(other);
		relationships.remove(other);
		lastSplitTimestamp = Game.game.soulWorld.timeCounter;
	}
	
	public void onDeath(Soul s)
	{
		// this can't be happening...
		relationships.remove(s);
		shock(EVENT_DEATH);
	}
	
	public void shock(int type)
	{
		// nothing beats deciding to die, except a new relationship
		if(shockType == EVENT_SUICIDE && type != MOOD_HAPPY)
		{
			return;
		}
//		newRelationships.clear();
		shockTimer = new Accumulator(1500);
		shockType = type;
	}

	public boolean canStartRelationship(Soul other)
	{
		return relationships.size() < 2 && !oldRelationships.contains(other) && !relationships.contains(other);
	}
}
